/***************
Submit By Mapo
Time：2022.2.21
***************/

/**************************************
					SPI
	同步全双工数据总线（单主设备）
	一根数据线和一个时钟信号来保证发送端和接收段的完美同步
	主机：产生时钟信号
	数据采集可以是时钟信号的上升沿或下降沿，自行配置
**************************************/

/**************************************
	总线：
		MISO
		MOSI
		SCLK 串行时钟信号，主机发送	
		NSS	 片选信号，主机发送，控制与哪个从机通信，低电平有效
**************************************/


/**************************************
				传输过程
	主机将NSS信号拉低，宣布开始发数据
	接收端检测到时钟的边沿信号时，开始读取数据线上的信号（时钟随数据一起发送，所以指定数据的传输速度不重要）
	
	主机->从机：主机产生相应的时钟信号，然后将数据从MOSI口发出
	从机->主机：主机产生预定数量的时钟信号，主机发送一个空字节宣布从机发送数据，然后从机将数据从MISO口发出
**************************************/


/**************************************
				时钟配置
	主机必须在通信开始时配置并生成相应的时钟信号，每个时钟周期内，都由全双工数据传输
	发送一位读取一位，无论接收任何数据，都必须实际发送一些数据
	时钟速率受限于系统的最大系统时钟频率和最大SPI传输速率
	
	时钟极性 CKP/Clock Polarity
	时钟极性和相位共同决定读取数据的方式，上升沿或下降沿读取
	参考设备的数据手册设置
	CKP=0：时钟空闲IDLE为低电平0
	CKP=1：时钟空闲IDLE为高电平0
	
	时钟相位 CKE/Clock Phase
	在哪一个边沿采集数据
	CKE=0：在时钟信号SCK的第一个跳变沿采样
	CKE=1：在时钟信号SCK的第二个跳变沿采样
	
	SPI时钟极性和相位的配置称为SPI模式配置
	
	SPI Mode		CPOL		CPHA
	 0[00]			 0			 0
	 1[01]			 0			 1
	 2[10]			 1			 0
	 3[11]			 1			 1
**************************************/


/**************************************
			多从机模式
	方法1：给每个从机一个NSS，需要给哪个发送就打开对应的片选信号，某一时刻只能由一个从机
	方法2：利用SPI移位寄存器，主机发给从机1，从机1发给从机2，从机2发给从机3，从机3的MISO口接主机的MISO口，
		   缺点：信号串行传输，中间某一从机瘫痪，后面的设备则不可通信
**************************************/

/**************************************
			SPI的工作模式
	运行模式
	等待模式：可配置的低功耗模式，SPICR2寄存器的SPISWAI位进行控制
		SPISWAI置0：类似于运行模式
		SPISWAI置1：SPI进入低功耗模式。
			配置主机：所有传输将停止，在CPU进入运行模式后重新开始
			配置从机：会继续接收和传输一个字节，保证主从同步
**************************************/



/**************************************
			针对STM32 
	兄弟们，很easy啊，加油
**************************************/


/********************************
						SPI2
	引脚：
	GPIO_Pin_12		NSS	
	GPIO_Pin_13 	SCLK
	GPIO_Pin_14 	MISO
	GPIO_Pin_15		MOSI
********************************/




#include "spi.h"



void SPI2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	SPI_InitTypeDef SPI_InitStruct;
	
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStruct.GPIO_Mode =	GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);

	//默认上拉
	GPIO_SetBits(GPIOB,GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
	
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;							
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;	
	SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_CRCPolynomial = 7;
	SPI_Init(SPI2,&SPI_InitStruct);
	SPI_Cmd(SPI2,ENABLE);
	
}

u8 SPI2_ReadWriteByte(u8 data)
{
	//u8 retry = 0;				//可以通过那个判断CRC标志位，如果错误就retry，可以定义retry的次数
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET);//判断发送标志位是否为0
	SPI_I2S_SendData(SPI2,data);
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET);//等待接收完一Byte
	return SPI_I2S_ReceiveData(SPI2);
}

/***************************虽然这里是针对NRF模块，但可以理解过程*************************************************************/
/**************************先写入寄存器地址和操作位，回读判断是否正确，在发数据***********************************************/



void SPI2_Write_Byte(u8 Reg,u8 data)
{
	u8 state;
	NSS_L;										//片选拉低，开始传输
	state = SPI2_ReadWriteByte(Reg);		//接收回读数据
	SPI2_ReadWriteByte(data);
	NSS_H;										//片选拉低，传输完成
}


u8 SPI2_Read_Byte(u8 Reg)
{
	u8 Value;
	NSS_L;										//片选拉低，开始传输
	SPI2_ReadWriteByte(Reg);		//这里接收的回读数据是状态寄存器的数据
	Value = SPI2_ReadWriteByte(0);		//这里才是
	NSS_H;										//片选拉低，传输完成
	return Value;
}

u8 SPI2_Write_Buf(u8 Reg,u8 *Buf,u8 Len)
{
	u8 state;
	NSS_L;
	state = SPI2_ReadWriteByte(Reg);
	
	while(Len)
	{
		SPI2_ReadWriteByte(*Buf);
		Buf++;
		Len--;
	}
	NSS_H;
	return state;
}


u8 SPI2_Read_Buf(u8 Reg,u8 *Buf,u8 Len)
{
	u8 state;
	NSS_L;
	state = SPI2_ReadWriteByte(Reg);
	
	while(Len)
	{
		*Buf = SPI2_ReadWriteByte(0);
		Buf++;
		Len--;
	}
	NSS_H;
	return state;
}



